﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Bamboo.Utils
{
    public class PathMerger
    {
        /// <summary>
        /// 源根目录
        /// </summary>
        public DirectoryInfo SrcBaseDir { get; set; }
        /// <summary>
        /// 目标根目录
        /// </summary>
        public DirectoryInfo DestBaseDir { get; set; }

        public event Action<string> LogHandler;
        public event Action<string> LogErrorHandler;
        /// <summary>
        /// 开启明细日志
        /// </summary>
        public bool LogDetailEnabled = false;

        /// <summary>
        /// 目标目录新增的文件
        /// </summary>
        public List<FileInfo> DestNewFiles { get; } = new List<FileInfo>();
        /// <summary>
        /// 目标目录新增的目录
        /// </summary>
        public List<DirectoryInfo> DestNewDirs { get; } = new List<DirectoryInfo>();
        /// <summary>
        /// 目标目录被覆盖的文件
        /// </summary>
        public List<FileInfo> DestOverwritedFiles { get; } = new List<FileInfo>();

        private FileDiff _diff = new FileDiff(1024 * 1024);


        private void Log(string s)
        {
            LogHandler?.Invoke(s);
        }

        private void LogDetail(string s)
        {
            if (LogDetailEnabled && LogHandler != null)
            {
                LogHandler(s);
            }
        }

        private void LogError(string s)
        {
            LogErrorHandler?.Invoke(s);
        }

        private void LogTitle(string s)
        {
            Log(">>>>>>>> " + s);
        }

        public string GetRelativePath(string path, string basePath)
        {
            var v1 = Path.GetFullPath(path).TrimEnd('\\');
            var v2 = Path.GetFullPath(basePath).TrimEnd('\\');
            if (!v1.StartsWith(v2))
                throw new Exception("path不是basepath的子目录：path=" + v1 + ", basepath=" + v2);
            var r = v1.Substring(v2.Length).TrimStart('\\');
            return r;
        }

        public FileInfo ToDestFile(FileInfo srcFile)
        {
            string r = GetRelativePath(srcFile.FullName, SrcBaseDir.FullName);
            string destFilename = Path.Combine(DestBaseDir.FullName, r);
            var v = new FileInfo(destFilename);
            return v;
        }

        private bool InnerCopyFile(FileInfo src, FileInfo dest, bool overwrite)
        {
            if (dest.Exists)
            {
                if (overwrite)
                {
                    if (_diff.IsDiff(src, dest))
                    {
                        //LogDetail($"覆盖文件：{src.FullName} => {dest.FullName}");
                        src.CopyTo(dest.FullName, true);
                        DestOverwritedFiles.Add(dest);
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                if (!dest.Directory.Exists)
                {
                    CreateDir(dest.Directory);
                }
                //LogDetail($"复制文件：{src.FullName} => {dest.FullName}");
                src.CopyTo(dest.FullName);
                DestNewFiles.Add(dest);
                return true;
            }

        }

        public bool CopyFile(FileInfo src, FileInfo dest, bool overwrite)
        {
            if (overwrite)
                LogDetail("拷贝文件(覆盖目标)：src=" + src.FullName + ", dest=" + dest.FullName);
            else
                LogDetail("拷贝文件(不覆盖目标)：src=" + src.FullName + ", dest=" + dest.FullName);
            var ret = InnerCopyFile(src, dest, overwrite);
            return ret;
        }

        private void CreateDir(DirectoryInfo dir)
        {
            Log($"创建目录：{dir.FullName}");
            dir.Create();
            DestNewDirs.Add(dir);
        }

        /// <summary>
        /// 合并文件到目标目录
        /// </summary>
        /// <param name="srcFile"></param>
        /// <param name="overwrite">若目标文件已存在，是否覆盖</param>
        /// <param name="deleteSrcFile">合并完成后是否删除源文件（若为true，即使未覆盖目标文件也删除）</param>
        /// <returns></returns>
        public bool MergeFile(FileInfo srcFile, bool overwrite, bool deleteSrcFile)
        {
            LogDetail($"合并文件：{srcFile.FullName}, 覆盖={overwrite}, 删除源文件={deleteSrcFile}");
            if (!srcFile.Exists) return false;
            bool ret = false;
            var destFile = ToDestFile(srcFile);

            CopyFile(srcFile, destFile, overwrite);

            if (deleteSrcFile)
            {   //删除源文件
                LogDetail($"删除源文件：" + srcFile.FullName);
                srcFile.Delete();
            }

            return ret;
        }

        /// <summary>
        /// 合并文件到目标目录
        /// </summary>
        /// <param name="srcFilename"></param>
        /// <param name="overwrite">若目标文件已存在，是否覆盖</param>
        /// <param name="deleteSrcFile">合并完成后是否删除源文件（若为true，即使未覆盖目标文件也删除）</param>
        /// <returns></returns>
        public bool MergeFile(string srcFilename, bool overwrite, bool deleteSrcFile)
        {
            var file = new FileInfo(srcFilename);
            return MergeFile(file, overwrite, deleteSrcFile);
        }

        /// <summary>
        /// 合并源目录下的多文件到目标目录
        /// </summary>
        /// <param name="srcPath"></param>
        /// <param name="pattern"></param>
        /// <param name="searchOption"></param>
        /// <param name="overwrite">若目标文件已存在，是否覆盖</param>
        /// <param name="deleteSrcFile">合并完成后是否删除源文件（若为true，即使未覆盖目标文件也删除）</param>
        /// <returns></returns>
        public int MergeFiles(string srcPath, string pattern, SearchOption searchOption, bool overwrite, bool deleteSrcFile)
        {
            Log($"合并目录：源目录={srcPath}, 搜索={pattern}, 选项={searchOption}, 覆盖={overwrite}, 删除源文件={deleteSrcFile}");
            if (!Directory.Exists(srcPath)) return 0;
            int moveCount = 0;
            if (string.IsNullOrWhiteSpace(srcPath))
                srcPath = SrcBaseDir.FullName;
            var dir = new DirectoryInfo(srcPath);
            var files = dir.GetFiles(pattern, searchOption);
            foreach (var file in files)
            {
                var succ = MergeFile(file, overwrite, deleteSrcFile);
                if (succ) ++moveCount;
            }
            return moveCount;
        }

        public int CopyFiles(string srcPath, string pattern, SearchOption searchOption, string destPath, bool overwrite)
        {
            Log($"复制目录：源目录={srcPath}, 目标目录={destPath}, 关键词={pattern}, 递归={searchOption}, 覆盖源文件={overwrite}");
            if (!Directory.Exists(srcPath)) return 0;
            int copyCount = 0;

            var srcDir = new DirectoryInfo(srcPath);
            var destDir = new DirectoryInfo(destPath);
            var files = srcDir.GetFiles(pattern, searchOption);
            foreach (var file in files)
            {
                var relativeFilename = GetRelativePath(file.FullName, srcDir.FullName);
                var destFilename = Path.Combine(destDir.FullName, relativeFilename);
                var destFile = new FileInfo(destFilename);

                CopyFile(file, destFile, overwrite);
            }

            return copyCount;
        }

        public void RemoveFiles(string path, string pattern, SearchOption searchOption)
        {
            Log($"删除文件：目录={path}, 关键词={pattern}, 递归={searchOption}");
            var dir = new DirectoryInfo(path);
            if (!dir.Exists) return;
            var files = dir.GetFiles(pattern, searchOption);
            foreach (var file in files)
            {
                //LogDetail("删除文件：" + file.FullName);
                file.Delete();
            }
        }
    }
}
